/*	This file is part of AstroAttack.
*
*	AstroAttack is free software; you can redistribute it and/or modify
*	it under the terms of the GNU General Public License as published by
*	the Free Software Foundation; either version 2 of the License, or
*	(at your option) any later version.
*	
*	AstroAttack is distributed in the hope that it will be useful,
*	but WITHOUT ANY WARRANTY; without even the implied warranty of
*	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*	GNU General Public License for more details.
*	
*	You should have received a copy of the GNU General Public License
*	along with AstroAttack; if not, write to the Free Software
*	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*	
*	$Id: Engine.java 34 2006-12-27 23:30:56Z xdracox $
*/

/**	The AstroAttack package contains all the classes that belong to the
*	AstroAttack program.
*/
package AstroAttack;

import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Color;

import java.awt.image.BufferStrategy;

/**	The Engine class manages the updating of all Entities. This class is a
*	singleton, only one instance of if exists at a time.
*/
public class Engine implements Runnable
{
	/**	The single instance of the class. */
	private static Engine m_ref;
	
	/**	This method returns the instance or initializes if it hasn't been
	*	initialized.
	*	@param	Frame	Frame the Engine will use to draw on
	*	@return	the instance of Engine
	*/
	public static Engine getEngine(Frame frame)
	{
		if ( m_ref == null )
			m_ref = new Engine(frame);
		return m_ref;
	}
	
	/** This method returns the already initialized instance or null if
	*	it has not been initialized.
	*	@param	the instance of Engine
	*	@return	the instane of Engine or null if it has not been initialized
	*/
	public static Engine getEngine()
	{
		return m_ref;
	}
	
	/**	The frame the Engine draws on. */
	private Frame m_frame;
	/**	The buffer strategy used by the frame. */
	private BufferStrategy m_strategy;
	/**	Thread used by the Engine. */
	private Thread m_thread;
	/**	Wether or not the Engine is running. */
	private boolean m_running;
	/**	The frame rate the engine should run at. */
	private int m_frameRate;
	
	/** Constructor
	*	@param	frame	the frame to draw on
	*/
	private Engine(Frame frame)
	{
		m_frame = frame;
		m_frame.setVisible(true);
		
		// default fps to 30
		m_frameRate = 30;
		
		// setup double buffering
		if ( m_frame.getBufferStrategy() == null )
		{
			m_frame.createBufferStrategy(2);
			m_strategy = m_frame.getBufferStrategy();
		}
		
		m_thread = new Thread(this);
	}
	
	/**	Sets the frame rate.
	*	@param	frameRate	the new frame rate
	*/
	public void setFrameRate(int frameRate)
	{
		m_frameRate = frameRate;
	}
	
	/**	Returns the frame rate
	*	@return	the frame rate
	*/
	public int getFrameRate()
	{
		return m_frameRate;
	}
	
	/**	Starts the engine if it has not yet been started. */
	public void start()
	{
		Debug.println("Starting engine:");
		
		try
		{
			m_running = true;
			m_thread.start();
			Debug.println("\tstarted successfully");
		}
		catch ( IllegalThreadStateException e )
		{
			Debug.println("\tengine already running!");
		}
	}
	
	/**	Stops the engine if it's not already stopped. */
	public void stop()
	{
		Debug.println("Stopping engine:");
		if ( m_thread.getState() == Thread.State.TERMINATED )
		{
			Debug.println("\tengine already stopped!");
		}
		else
		{
			try
			{
				m_running = false;
				m_thread.join();
				Debug.println("\tengine stopped successfully");
			}
			catch ( InterruptedException e )
			{
				Debug.println("\tthread interrupted while waiting for engine to stop!");
				System.exit(0);
			}
		}
	}
	
	/**	The run method runs the game loop. */
	public void run()
	{
		while ( m_running )
		{
			Graphics2D gfx = (Graphics2D)m_strategy.getDrawGraphics();
			
			/**	@todo	there should probably be some sort of display manager
			*			that manages what gets drawn everytime in the
			*			background, for now we'll just fill it in black
			*/
			
			gfx.dispose();
			m_strategy.show();
			
			long sleepTime = 1000 / m_frameRate;
            // let the thread sleep, this'll allow us to hit the target
            // fps as close as possible
            try
            {
            	Thread.sleep(sleepTime);
            }
            catch ( InterruptedException e )
            {
            	Debug.println("Thread interrupted while sleeping... continuing");
            }
		}
	}
}
